Skip to content

callcenter: D-ODOO-SAV-4 odoo-savant reasoners + audit/Policy fixes#420

Merged
AdaWorldAPI merged 10 commits into
mainfrom
claude/activate-lance-graph-att-k2pHI
May 28, 2026
Merged

callcenter: D-ODOO-SAV-4 odoo-savant reasoners + audit/Policy fixes#420
AdaWorldAPI merged 10 commits into
mainfrom
claude/activate-lance-graph-att-k2pHI

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented May 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • D-ODOO-SAV-4 — odoo-savant Reasoner layer (crates/lance-graph-callcenter/src/savant_reasoners.rs): SavantConclusion { savant_id, query_strategy, confidence: NarsTruth, rationale } (suggestion-only, serde-free) + the 4 one-impl-per-ReasoningKind reasoners — CustomerCategoryReasoner / PostingAnomalyReasoner / NextBestActionReasoner / OtherReasoner — covering all 25 savants in contract::savants::SAVANTS, per the dispatch pinned in docs(odoo-savants): 25 AXIS-B evidence contracts (carve-out) + dispatch decision #419. Each resolves the concrete savant from (kind, namespace), selects its QueryStrategy via InferenceType::default_strategy(), and fuses evidence-ref coverage into a NARS (frequency, confidence).
  • Dispatch resolution lives in callcenter — the contract stays an untouched inheritance vow (no namespace field added to Savant). resolve_savant filters the roster by kind; ambiguous kinds split via DISPATCH_NS (the Other(RECONCILE_MATCH) 19-vs-21 namespace split per docs(odoo-savants): 25 AXIS-B evidence contracts (carve-out) + dispatch decision #419) then by namespace == savant.name.
  • with_jsonl_auditResult<Self, AuditError> (was io::Result): feature-gated audit errors surface as the domain error, not a lossy io::Error coercion.
  • callcenter re-exports Policy + smb_policy so consumers barred from a direct lance-graph-rbac dependency can build a UnifiedBridge through the facade alone.

Architecture: one-binary contract — SavantConclusion is a plain in-binary value that woa-rs consumes as the same struct; nothing is serialized between them. JSON exists only at the callcenter↔MedCareV2 FFI boundary, never on these types. lance-graph does the thinking (the ambiguous AXIS-B core); woa-rs keeps the deterministic AXIS-A guard and consumes the suggestion.

Scope note: all 25 savants dispatch through the 4 impls today; the 14 NEEDS-INPUT savants are blocked on woa-rs evidence feeds, not on the impl — their row-level column fusion lands when woa-rs supplies materialized evidence (v1 fusion is coverage-based + monotone).

Test plan

  • cargo test -p lance-graph-callcenter --features jsonl — 8 new savant_reasoners tests + 137 prior pass
  • zone_serialize_check (no-JSON guard) clean — confirms the module is serde-free
  • Covered: savant resolution, RECONCILE_MATCH namespace split, single-candidate, strategy↔inference, monotone confidence, async-trait dispatch, kind-mismatch

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added a set of suggestion-only "savant" reasoners and exposed their public types through the callcenter facade, enabling structured suggestions and confidence/rationale reporting.
  • Bug Fixes

    • Fixed a CI/build error when the jsonl feature is enabled by correcting the audit constructor's error return type.
  • Documentation

    • Added integration proposal for multi-server scaling, tech-debt entries on serialization and episodic retrieval, and several board/epiphany log entries.

Review Change Stack

claude added 9 commits May 26, 2026 20:23
The jsonl-feature build broke: JsonlAuditSink::new returns
Result<_, AuditError>, but with_jsonl_audit was typed io::Result<Self> and
no From<AuditError> for io::Error exists, so the `?` couldn't convert
(E0277). The default-feature check skips this path; CI builds the feature
and surfaced it.

Return the honest error type (Result<Self, AuditError>) rather than adding a
crate-wide AuditError -> io::Error coercion. Zero callers depend on the old
io::Result signature (only a doc-comment mention), so this breaks nothing.

cargo check/test -p lance-graph-callcenter --features jsonl clean (137 tests).
…erialization sites

Two PREPEND entries to TECH_DEBT.md:

- TD-ARIGRAPH-EPISODIC-FIDELITY-1: the Python→Rust AriGraph transcode
  reimplemented episodic retrieval as the fingerprint-RAG baseline the paper
  (arxiv 2407.04363) outperforms — eq.1 structural relevance and the episodic
  edge E_e (the dropped witness_ref/W-slot) are absent; three disconnected
  episodic/provenance stores exist. Options documented: A (narrow in-place
  eq.1 fix) vs B (mailbox/W-slot convergence = the already-planned D-CSV-6
  WitnessCorpus + D-CSV-7 MailboxSoA W-slot).

- TD-JSON-SERIALIZATION-SITES-1: catalogs serde/JSON occurrences under the
  single-binary no-serialization invariant — outer-boundary ingestion
  (Cypher/AST, config, JWT, REST, query params) is correct by design; the
  debt is serde-on-substrate (arigraph orchestrator/sensorium/TruthValue) and
  JSON audit egress (jsonl_sink + lance_sink JSON-in-column), whose canonical
  form is the binary canonical_bytes. Reframes TD-SDR-AUDIT-PERSIST-1.
…ack argument + expansion path

New plan documenting why the Rust/Raft/lance stack displaces JanusGraph,
Cassandra, and Zitadel for multi-server HA/scale-out, plus the conditional
expansion architecture:

- Displacement: distributed graph + CP via SurrealDB-on-TiKV + lance-graph
  (vs JanusGraph's JVM 3-system assembly); Cassandra's AP is the wrong
  consistency model for a belief substrate; authN/IdP gap filled by Ory
  (Kratos + Hydra), binary stays verification-only.
- Multi-server: Raft replicates the belief-delta / episodic LOG; the zero-copy
  SoA is the per-node state machine; the Rubikon commit_gate is the Raft append
  point. Resolves TiKV-vs-lance as TiKV-the-log UNDER lance-the-state.
- Hard prerequisite (unproven): byte-deterministic NARS apply
  (reencode_safety / D-SDR-26); next deliverable is the determinism probe.

PROPOSAL/expansion-readiness; single-node needs none of it. Indexed in
INTEGRATION_PLANS.md.
…-1 + PR_ARC entry

Closes the board-hygiene gap: the May 26 signature fix (io::Result -> AuditError,
form 1 / W2's instinct) shipped touching only the .rs file; the board still
recorded only PR #366's introduction of the constructor. E-AUDIT-1 captures the
generalizable lesson (default-feature cargo check masks feature-gated E0277s;
error-type honesty over lossy From-coercion).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
UnifiedBridge::new takes an Arc<lance_graph_rbac::policy::Policy>, but some
consumers are barred from a direct lance-graph-rbac dependency (e.g. woa-rs's
BBB-barrier allow-list is contract / ontology / callcenter only). Surface the
Policy type and the smb_policy() starter factory through the callcenter facade
so those consumers can build a UnifiedBridge without naming rbac directly.
lance-graph-rbac is already an internal dependency here, so this is a zero-cost
re-export.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
…-graph-att-k2pHI

# Conflicts:
#	.claude/board/EPIPHANIES.md
#	.claude/board/INTEGRATION_PLANS.md
#	.claude/board/PR_ARC_INVENTORY.md
…per ReasoningKind)

Implement the lance-graph "thinking" side of the 25-savant delegation:
SavantConclusion (suggestion-only, serde-free) + CustomerCategoryReasoner /
PostingAnomalyReasoner / NextBestActionReasoner / OtherReasoner — the pinned
one-impl-per-ReasoningKind dispatch (PR #419) covering all 25 savants in
contract::savants::SAVANTS.

Each reasoner resolves the concrete savant from (kind, namespace), selects its
QueryStrategy via InferenceType::default_strategy(), and fuses evidence-ref
coverage into a NARS (frequency, confidence). woa-rs consumes the conclusion as
a native shared type (one-binary contract) and keeps the deterministic AXIS-A
guard; this is the ambiguous AXIS-B core only.

Dispatch resolution lives in callcenter — the contract stays an untouched
inheritance vow (no namespace field on Savant). resolve_savant filters the
roster by kind; ambiguous kinds split via DISPATCH_NS (the Other(RECONCILE_MATCH)
19-vs-21 namespace split per #419) then by namespace == savant.name.

No serialization in scope: SavantConclusion is a plain in-binary value; JSON
exists only at the callcenter<->MedCareV2 FFI boundary.

8 new tests; 137 prior callcenter tests pass; zone_serialize_check no-JSON guard
clean. AGENT_LOG updated (board-hygiene).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
… deny-list)

A workspace bouncer that pushes comprehension to the Read tool (not cat/head/
tail), Edit (not sed), and Glob/Read or rg (not grep):

- .claude/hooks/forbid-grep-sed-head-tail.sh — PreToolUse(Bash) hook that denies
  any command whose leading token (per ;|&() segment) is grep/egrep/fgrep/sed/
  head/tail. Command-position-only (no false positives on filenames/substrings),
  fail-open (allows if jq missing), and uses no banned tool itself.
- .claude/agents/forbidden-tool-warden.md — audit agent for the hook's blind
  spots (committed scripts, Makefiles, CI, the hooks dir), files offenders to
  the board; obeys the ban itself (Glob + Read + rg).
- .claude/settings.json — wire the PreToolUse hook + add Bash(grep|sed|head|
  tail|egrep|fgrep) to the deny list.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a deterministic AXIS-B savant reasoner layer (suggestion-only conclusions, dispatch by kind/namespace, evidence→NARS fusion), exposes reasoners and RBAC policy via the callcenter facade, includes unit tests, and adds multi-server replication proposal and board entries.

Changes

Savant Reasoners Implementation and Multi-Server Framework

Layer / File(s) Summary
Savant reasoner types and dispatch logic
crates/lance-graph-callcenter/src/savant_reasoners.rs
SavantError, SavantSuggestion, SavantConclusion (suggestion-only with savant id, QueryStrategy, NarsTruth, rationale), DISPATCH_NS, kind_matches, resolve_savant(), build_conclusion(), and reason_for_kind() orchestration.
Reasoner trait implementations & tests
crates/lance-graph-callcenter/src/savant_reasoners.rs
Async Reasoner impls: CustomerCategoryReasoner, PostingAnomalyReasoner, NextBestActionReasoner, OtherReasoner (accepts only ReasoningKind::Other(_)); unit tests for dispatch disambiguation, strategy derivation, evidence/ confidence behavior, async dispatch, and coverage.
Library facade integration
crates/lance-graph-callcenter/src/lib.rs
Adds pub mod savant_reasoners;, re-exports reasoner types and shared types, and re-exports RBAC policy API smb_policy and Policy from lance_graph_rbac.
Implementation documentation and audit tracking
.claude/board/AGENT_LOG.md, .claude/board/EPIPHANIES.md, .claude/board/PR_ARC_INVENTORY.md
D-ODOO-SAV-4 agent log documents the reasoner-layer shape and dispatch; E-AUDIT-1 records the with_jsonl_audit feature-gated error fix; PR arc records the with_jsonl_audit return-type change.
Multi-server expansion proposal and integration plan
.claude/plans/multi-server-cognition-expansion-v1.md, .claude/board/INTEGRATION_PLANS.md
New proposal and integration-plan index proposing Raft-replicated belief-delta log with local zero-copy SoA state, commit_gate append boundary, byte-deterministic apply prerequisite, phased readiness plan, and DECIDED: Ory for authN.
Technical debt tracking
.claude/board/TECH_DEBT.md
Prepends TD-ARIGRAPH-EPISODIC-FIDELITY-1 and TD-JSON-SERIALIZATION-SITES-1 entries documenting episodic retrieval transcode debt and internal serde/JSON usage debt.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AdaWorldAPI/lance-graph#416: Related earlier work around the savant roster, ReasoningKind/Other(u32) mappings, and query_strategy metadata used by the new savant_reasoners dispatcher.

Poem

🐰 I mapped the savants, quiet and bright,

Evidence stitched into NARS-lit light,
Four little reasoners hum in tune,
Belief-deltas queued beneath the moon,
A rabbit cheers—code ready to run soon.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the two main changes: addition of the Savant reasoners layer (D-ODOO-SAV-4) and fixes to audit error handling and Policy re-exports.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/activate-lance-graph-att-k2pHI

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87ab547b03

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +57 to +65
pub struct SavantConclusion {
/// Roster id of the savant that produced this (SAVANTS.md numbering).
pub savant_id: u8,
/// The query strategy the savant dispatches to (from its `InferenceType`).
pub query_strategy: QueryStrategy,
/// NARS `(frequency, confidence)` weight of the suggestion.
pub confidence: NarsTruth,
/// Human-readable rationale (the AXIS-B decision + evidence summary).
pub rationale: Cow<'static, str>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the missing suggestion payload

When woa-rs consumes one of these reasoners, the conclusion does not contain the actual AXIS-B decision to apply; it only returns the savant id, strategy, confidence, and rationale. The in-repo contract for this deliverable calls for SavantConclusion { suggestion, confidence: NarsTruth, rationale } (.claude/plans/odoo-savant-reasoners-v1.md:80), and individual savant specs require concrete payloads such as ResolveFiscalPosition(partner_id, fiscal_position_id) (.claude/odoo/savants/FiscalPositionResolver.md:84). In that scenario the caller can rank a result but cannot know which fiscal position, date, account, invoice match, etc. was suggested, so the new reasoner layer cannot perform the delegated decision.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.claude/plans/multi-server-cognition-expansion-v1.md (1)

92-96: 💤 Low value

Consider rewording to improve readability.

Three consecutive list items begin with "Ory". While this is a structured enumeration of Ory components, consider rephrasing for variety:

-- **Ory Hydra** = OAuth2 / OIDC server → token **issuance** (the piece the Raft
-  stack structurally lacks).
-- **Ory Kratos** = identity lifecycle → login, registration, MFA, recovery, user store.
-- **Ory Keto** = **not adopted** — authZ stays in-stack (`Policy` RBAC + `TenantId`
-  Chinese-wall + merkle audit); Keto's Zanzibar model would duplicate it.
+- **Hydra** (OAuth2/OIDC server) → token **issuance** (the piece the Raft
+  stack structurally lacks).
+- **Kratos** (identity lifecycle) → login, registration, MFA, recovery, user store.
+- **Keto** = **not adopted** — authZ stays in-stack (`Policy` RBAC + `TenantId`
+  Chinese-wall + merkle audit); Keto's Zanzibar model would duplicate it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/plans/multi-server-cognition-expansion-v1.md around lines 92 - 96,
The three list items ("Ory Hydra", "Ory Kratos", "Ory Keto") are repetitive and
reduce readability; rephrase each bullet so they don't all start with "Ory"
while preserving meaning — e.g., "Hydra — OAuth2/OIDC server (token issuance)",
"Kratos — identity lifecycle (login, registration, MFA, recovery, user store)",
and "Keto — not adopted — authZ stays in-stack (`Policy` RBAC + `TenantId`
Chinese-wall + merkle audit)". Ensure you keep the statements about Hydra
providing token issuance and Keto not adopted (with the `Policy` and `TenantId`
references) and maintain the original intent and emphasis.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/board/PR_ARC_INVENTORY.md:
- Around line 38-46: The board entry incorrectly claims "not yet a PR" although
this change is part of PR `#420` and violates the file's "Every **merged** PR" and
APPEND-ONLY rules; fix by editing the prepended entry in
.claude/board/PR_ARC_INVENTORY.md: either replace the "not yet a PR" text (the
tuple containing claude/activate-lance-graph-att-k2pHI and commit ea2a378) with
an explicit reference to "PR `#420`" and keep the rest intact, or remove/defer
this new entry entirely and add it in a post-merge housekeeping commit so the
file remains an immutable record.

---

Nitpick comments:
In @.claude/plans/multi-server-cognition-expansion-v1.md:
- Around line 92-96: The three list items ("Ory Hydra", "Ory Kratos", "Ory
Keto") are repetitive and reduce readability; rephrase each bullet so they don't
all start with "Ory" while preserving meaning — e.g., "Hydra — OAuth2/OIDC
server (token issuance)", "Kratos — identity lifecycle (login, registration,
MFA, recovery, user store)", and "Keto — not adopted — authZ stays in-stack
(`Policy` RBAC + `TenantId` Chinese-wall + merkle audit)". Ensure you keep the
statements about Hydra providing token issuance and Keto not adopted (with the
`Policy` and `TenantId` references) and maintain the original intent and
emphasis.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c7de932-196f-40f5-9a57-21b363ec3a69

📥 Commits

Reviewing files that changed from the base of the PR and between ce65566 and 87ab547.

📒 Files selected for processing (8)
  • .claude/board/AGENT_LOG.md
  • .claude/board/EPIPHANIES.md
  • .claude/board/INTEGRATION_PLANS.md
  • .claude/board/PR_ARC_INVENTORY.md
  • .claude/board/TECH_DEBT.md
  • .claude/plans/multi-server-cognition-expansion-v1.md
  • crates/lance-graph-callcenter/src/lib.rs
  • crates/lance-graph-callcenter/src/savant_reasoners.rs

Comment on lines +38 to +46
## callcenter/audit-fix — fix(callcenter): `with_jsonl_audit` returns `Result<Self, AuditError>` (branch work)

**Status:** On branch `claude/activate-lance-graph-att-k2pHI` (HEAD `ea2a378`, not yet a PR). 1-line `.rs` change + this board record (EPIPHANIES E-AUDIT-1, prepended 2026-05-27).

- **Added** — nothing new; retypes `UnifiedBridge::with_jsonl_audit` return from `std::io::Result<Self>` → `Result<Self, crate::audit_sink::AuditError>` (`unified_bridge.rs:315`). Resolves an E0277 that only the `--features jsonl` build surfaced (the default `cargo check` skips the feature-gated path).
- **Locked** — audit constructors return the **domain** error (`AuditError`), not `io::Result`. **No** crate-wide `From<AuditError> for std::io::Error` coercion (rejected: lossy across the non-`Io` variants `ChannelFull`/`Serialize`/`SchemaMigration`/`Lance`/`Arrow`). Optional-feature error paths must be CI-checked under their feature, not just default-feature (E-AUDIT-1).
- **Deferred** — none. MedCare-rs sprint-2 item 5 (first real caller) consumes the `AuditError` signature directly; any caller needing `io::Result` interop adds a local `map_err`, not a crate-wide `From`.
- **Docs** — EPIPHANIES E-AUDIT-1.
- **Confidence (2026-05-27):** working — `cargo check/test -p lance-graph-callcenter --features jsonl` clean at commit time (137 tests); tree clean; zero callers depend on the old signature.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Status claim inconsistent with PR context and file governance rules.

Line 40 states "not yet a PR" but this entry is being added as part of PR #420 (per PR objectives). Additionally, the file header (lines 3-6) states this tracks "Every merged PR", yet this entry is being prepended pre-merge.

This violates the historical-record contract:

  • Pre-merge entries risk becoming stale if the PR changes or is abandoned
  • The APPEND-ONLY rule (line 8) prevents correction beyond confidence annotations
  • The "not yet a PR" claim contradicts the current PR #420 context

Recommendation: Either update line 40 to reference PR #420 explicitly, or defer adding this entry until after PR #420 merges. The append-only discipline works best when entries are truly immutable historical facts, not in-flight documentation.

Suggested fix
-**Status:** On branch `claude/activate-lance-graph-att-k2pHI` (HEAD `ea2a378`, not yet a PR). 1-line `.rs` change + this board record (EPIPHANIES E-AUDIT-1, prepended 2026-05-27).
+**Status:** In PR `#420` (branch `claude/activate-lance-graph-att-k2pHI`, HEAD `ea2a378`). 1-line `.rs` change + this board record (EPIPHANIES E-AUDIT-1, prepended 2026-05-27).

Alternatively, consider moving this entire entry addition to a post-merge housekeeping commit so it becomes a true historical record per the file's stated purpose.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/board/PR_ARC_INVENTORY.md around lines 38 - 46, The board entry
incorrectly claims "not yet a PR" although this change is part of PR `#420` and
violates the file's "Every **merged** PR" and APPEND-ONLY rules; fix by editing
the prepended entry in .claude/board/PR_ARC_INVENTORY.md: either replace the
"not yet a PR" text (the tuple containing claude/activate-lance-graph-att-k2pHI
and commit ea2a378) with an explicit reference to "PR `#420`" and keep the rest
intact, or remove/defer this new entry entirely and add it in a post-merge
housekeeping commit so the file remains an immutable record.

…d (P1 review on #420)

The SavantConclusion dropped the `suggestion` field the plan + per-savant specs
require (.claude/plans/odoo-savant-reasoners-v1.md:80,
.claude/odoo/savants/FiscalPositionResolver.md:84), so a consumer could rank by
confidence but not see *which* decision was delegated.

Add `pub enum SavantSuggestion` (callcenter type, contract untouched — the vow
stays pure, no serde / no JSON) with variants for the AXIS-B decision shapes
across the 25 savants:
- SelectFromTable { candidate_table } — top-1 entity (FiscalPosition, Pricelist,
  Account, AnalyticModel, ProcurementRule, RouteTiebreaker, BankStatementMatch)
- Gate — yes/no (Autopost, PaymentToInvoiceMatch, Upsell, BackorderJudge)
- Anomaly — suspect key (SequenceGap)
- AdvancePeriod — next open period (LockDate, ReorderTiming)
- PolicyChoice — fixed-set variant (TaxExigibility, ReportRateType,
  PartnerTrust, UserCompanyAccess, RemovalStrategy)
- Distribution { over_table } — weighted (AnalyticDistribution, ReplenishmentReport)
- RankedSet { from_table } — top-N (ReconcileMatch, MoveAssignmentPrioritizer,
  CurrencySelection)

`suggestion_for(savant)` maps every roster id → its variant; reasoners populate
it via `build_conclusion`. The row-level value (the concrete fiscal_position_id,
etc.) is the consumer's mechanical resolution over rows it holds — the same
boundary the 14 NEEDS-INPUT savants already document. The conclusion now
CARRIES the AXIS-B decision shape so the delegation is actionable.

Tests: 2 new (suggestion_shape_per_savant + every_savant_has_a_suggestion_shape
covering all 25 roster ids); 8 prior savant_reasoners tests pass; 137 callcenter
tests pass; zone_serialize_check no-JSON guard clean.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv

Copy link
Copy Markdown
Owner Author

Addressed the P1 in 4162689 — added SavantSuggestion (callcenter enum, contract untouched, no serde) and wired the suggestion: SavantSuggestion field on SavantConclusion. suggestion_for(savant) maps every roster id → its AXIS-B decision shape:

  • SelectFromTable { candidate_table } — top-1 entity (Fiscal/Pricelist/Account/AnalyticModel/ProcurementRule/RouteTiebreaker/BankStatementMatch)
  • Gate — yes/no (Autopost / PaymentToInvoiceMatch / Upsell / BackorderJudge)
  • Anomaly — suspect key (SequenceGap)
  • AdvancePeriod — next open period (LockDate / ReorderTiming)
  • PolicyChoice — fixed-set variant (TaxExigibility / ReportRateType / PartnerTrust / UserCompanyAccess / RemovalStrategy)
  • Distribution { over_table } — weighted (AnalyticDistribution / ReplenishmentReport)
  • RankedSet { from_table } — top-N (ReconcileMatch / MoveAssignmentPrioritizer / CurrencySelection)

Honest boundary, called out in the doc-comment: the row-level value (the concrete fiscal_position_id, the holds: bool, etc.) is the consumer's mechanical resolution over rows it holds — EvidenceRef is a metadata handle (table + schema_fingerprint + rows, no columns/ids by contract), so this layer carries the decision shape + candidate-evidence source, and the consumer applies it. That's the same boundary the 14 NEEDS-INPUT savants already document — full row-level value resolution lands when materialized evidence flows. The conclusion now carries the AXIS-B decision shape so the delegation is actionable.

Two new tests (10 total): suggestion_shape_per_savant covers Fiscal/SequenceGap/Autopost/PaymentToInvoiceMatch/ReconcileMatch/LockDate/AnalyticDistribution; every_savant_has_a_suggestion_shape is the defensive coverage gate over all 25 roster ids. 137 prior callcenter tests pass; zone_serialize_check no-JSON guard clean.


Generated by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/lance-graph-callcenter/src/savant_reasoners.rs (1)

183-186: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve savant-name fallback when DISPATCH_NS entry doesn’t match this kind.

Line [183]-Line [185] returns early even when the mapped ID is not in candidates, which bypasses the subsequent namespace == savant.name resolution path.

Proposed change
-            if let Some(&(_, id)) = DISPATCH_NS.iter().find(|(ns, _)| *ns == namespace) {
-                return candidates.iter().copied().find(|s| s.id == id);
-            }
+            if let Some(&(_, id)) = DISPATCH_NS.iter().find(|(ns, _)| *ns == namespace) {
+                if let Some(s) = candidates.iter().copied().find(|s| s.id == id) {
+                    return Some(s);
+                }
+            }
             candidates.iter().copied().find(|s| s.name == namespace)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lance-graph-callcenter/src/savant_reasoners.rs` around lines 183 -
186, The current code returns early when DISPATCH_NS maps the namespace to an id
even if that id is not present in candidates, skipping the fallback name match;
modify the logic in the block that checks DISPATCH_NS so you attempt to find a
candidate by the mapped id and only return if that find yields Some(s),
otherwise continue to the final lookup that finds by s.name == namespace (i.e.,
change the early return to conditional return based on whether
candidates.iter().copied().find(|s| s.id == id) is Some, and otherwise let the
subsequent candidates.iter().copied().find(|s| s.name == namespace) run).
🧹 Nitpick comments (1)
crates/lance-graph-callcenter/src/savant_reasoners.rs (1)

91-121: ⚡ Quick win

Avoid silent fallback in suggestion_for for unknown savant IDs.

Line [119] (_ => PolicyChoice) masks missing mappings and can return the wrong decision shape when roster IDs evolve.

Proposed change
 fn suggestion_for(savant: &Savant) -> SavantSuggestion {
     use SavantSuggestion::*;
     match savant.id {
@@
         25 => RankedSet { from_table: Cow::Borrowed("stock.move") },
         26 => Gate,
-        _ => PolicyChoice,
+        id => unreachable!("missing SavantSuggestion mapping for savant id {id}"),
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lance-graph-callcenter/src/savant_reasoners.rs` around lines 91 - 121,
The match in function suggestion_for silently falls back to `_ => PolicyChoice`,
which can mask missing mappings; change suggestion_for to return
Result<SavantSuggestion, UnknownSavantIdError> (or similar error type) instead
of raw SavantSuggestion, remove the `_ => PolicyChoice` arm, and replace it with
an explicit `Err(UnknownSavantId(savant.id))` for unmapped IDs; update callers
to handle the Result (or alternatively add a distinct SavantSuggestion::Unknown
variant and return that instead) so unknown savant IDs are not silently treated
as PolicyChoice.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/lance-graph-callcenter/src/savant_reasoners.rs`:
- Around line 183-186: The current code returns early when DISPATCH_NS maps the
namespace to an id even if that id is not present in candidates, skipping the
fallback name match; modify the logic in the block that checks DISPATCH_NS so
you attempt to find a candidate by the mapped id and only return if that find
yields Some(s), otherwise continue to the final lookup that finds by s.name ==
namespace (i.e., change the early return to conditional return based on whether
candidates.iter().copied().find(|s| s.id == id) is Some, and otherwise let the
subsequent candidates.iter().copied().find(|s| s.name == namespace) run).

---

Nitpick comments:
In `@crates/lance-graph-callcenter/src/savant_reasoners.rs`:
- Around line 91-121: The match in function suggestion_for silently falls back
to `_ => PolicyChoice`, which can mask missing mappings; change suggestion_for
to return Result<SavantSuggestion, UnknownSavantIdError> (or similar error type)
instead of raw SavantSuggestion, remove the `_ => PolicyChoice` arm, and replace
it with an explicit `Err(UnknownSavantId(savant.id))` for unmapped IDs; update
callers to handle the Result (or alternatively add a distinct
SavantSuggestion::Unknown variant and return that instead) so unknown savant IDs
are not silently treated as PolicyChoice.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 48ca60b7-6ec3-477f-9713-72a8cb4f7d77

📥 Commits

Reviewing files that changed from the base of the PR and between 87ab547 and 4162689.

📒 Files selected for processing (2)
  • crates/lance-graph-callcenter/src/lib.rs
  • crates/lance-graph-callcenter/src/savant_reasoners.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/lance-graph-callcenter/src/lib.rs

@AdaWorldAPI AdaWorldAPI merged commit 0754a76 into main May 28, 2026
6 checks passed
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…ition over CausalEdge64 + Tactic + role_keys (E-SAVANT-COMPOSITION-1)

v1 (shipped PR #420 with a "MED on dispatch shape" caveat) review resolved:
the Reasoner trait surface fails CLAUDE.md "P-1 The Click" + "P0 AGI-as-glove"
litmus tests verbatim:

1. "new capability lands as a new column, not a new layer"
   → the Reasoner trait IS a new layer
2. "free function on a carrier's state = reject"
   → build_conclusion(savant, ctx) is the named anti-pattern
3. "wrap the axes in a new struct = breaks the SIMD sweep"
   → SavantConclusion + SavantSuggestion duplicate CausalEdge64

v2 routes the canonical path through the agnostic substrate that already exists:
- CausalEdge64 (zero-dep crates/causal-edge, v2 layout — the savant's decision
  IS the emitted edge: SPO palette + NARS truth + Pearl 2³ + inference mantissa)
- Tactic trait + 34 kernels (PR #411 — "the Elixir-like recipe layer that later
  fronts the real fingerprint substrate via cognitive-shader-driver with no
  change to the 34 call sites")
- 33-TSV atom layer (PR #411 contract::atoms::CANONICAL_ATOMS)
- Role-key catalogues (I-VSA-IDENTITIES names callcenter/role_keys.rs as future
  Layer-2 home)

Deliverables (Queued):
- D-ODOO-SAV-5a: SavantPattern + TacticInvocation + EdgeEmissionSpec +
  AtomTouchMask primitives in lance-graph-contract (Group D)
- D-ODOO-SAV-5b: callcenter/role_keys.rs with 25 disjoint Vsa16kF32 slices
  + lookup-by-OdooSavant + slice manifest (Group E)
- D-ODOO-SAV-5c: 25 typed SavantPattern consts drawn from
  .claude/odoo/savants/<N>.md slot 1/4 + .claude/odoo/L*.md (Group F)
- D-ODOO-SAV-5d: #[deprecated] + legacy-reasoner feature gate + migration
  pointers on v1 Reasoner surface, per I-LEGACY-API-FEATURE-GATED (Group G)
- D-ODOO-SAV-5e: end-to-end test (FiscalPositionResolver SavantPattern →
  expected CausalEdge64 row, SPO + NARS + v2 signed mantissa)

Execution: 5a + 5b parallel (additive, zero churn) → 5c after both → 5d after
5c (migration pointers name real targets) → 5e throughout. woa-rs consumer
migration OUT OF SCOPE but UNBLOCKED by 5d.

Board hygiene per CLAUDE.md: plan file + INTEGRATION_PLANS PREPEND +
STATUS_BOARD section + EPIPHANIES E-SAVANT-COMPOSITION-1 all in this commit.

v1 surface (Reasoner trait, 4 *Reasoner impls, SavantConclusion,
SavantSuggestion, build_conclusion) stays compiling under the legacy-reasoner
feature with #[deprecated] migration pointers until woa-rs migrates its
Reasoner::reason() call sites to SavantPattern resolution. Removal in a
follow-up PR after the migration.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…OO-SAV-5b 25 savant role keys

Two parallel additive deliverables — both zero-churn surfaces opened by
the v2 reshape (PR #420 deprecation) + blueprint plan (commits 741a25c
+ 6d2466e):

## D-ODOO-BP-1a — typed Odoo entity DTOs (lance-graph-ontology::odoo_blueprint)

New module `crates/lance-graph-ontology/src/odoo_blueprint.rs` carrying
the typed surface the OGIT → OWL → DOLCE → FIBU/FIBO inheritance chain
will operate on (replacing today's ad-hoc string-keyed maps against
`model_name`).

Types: OdooEntity + OdooField + OdooMethod + OdooDecorator +
OdooStateMachine + OdooState + OdooTransition + OdooConstraint +
OdooProvenance + OdooSourceRef.

Enums: OdooFieldKind (17 variants), OdooSemanticRole (12), OdooMethodKind
(10), OdooReturnKind (10), OdooDecoratorKind (7), OdooStateSemantic (7),
OdooConstraintKind (3), OdooConfidence (3 — Curated/Extracted/Conjecture).

Tests: 3 — sample_entity_compiles_as_const (FiscalPosition shape),
state_machine_entity_compiles (Invoice draft→posted→cancel),
empty_entity_compiles (zero case).

Per-lane consts (L1–L15) land in D-ODOO-BP-1b; OGIT/OWL/DOLCE/FIBU
wiring lands in 1c/d/e; source extraction in 1f; JITson + recipes in 1g.

## D-ODOO-SAV-5b — 25 savant role keys (contract::callcenter::role_keys)

New module `crates/lance-graph-contract/src/callcenter/{mod,role_keys}.rs`
per I-VSA-IDENTITIES Layer-2 catalogue doctrine (sibling of
`contract::grammar::role_keys`, NOT in the separate lance-graph-callcenter
crate — path correction from the v2 plan).

Slice layout: 25 savants × 90 dims = 2250 dims in the SMB headroom
[14096..16346), with 38 dims of headroom remaining. FNV-64 seeded from
each savant's name (lookup matches SAVANTS const order). LazyLock<[RoleKey; 25]>
lookup by id or name.

Tests: 7 — slices_disjoint_and_in_bounds, savant_zone_fits_in_smb_headroom
(2250 dims + 38 headroom = 2288), id_lookup_matches_name_lookup,
id_16_absent (the intentional gap), last_savant_is_backorder_judge,
deterministic_pseudo_random_bits, no_overlap_with_grammar_slices.

## Supporting change

`RoleKey::generate` made `pub` in `contract::grammar::role_keys` so
sibling per-domain Layer-2 catalogues (callcenter today; persona, others
later) can construct their own role keys with disjoint slice allocations
— per I-VSA-IDENTITIES. Documented in the doc-comment.

## Tests + integration

- lance-graph-contract: 7 new callcenter::role_keys tests pass; 454 prior
  contract lib tests unaffected
- lance-graph-ontology: 3 new odoo_blueprint tests pass; 43 lib tests total

## Followups

- D-ODOO-SAV-5a (BusinessGrammarTemplate primitive) — design pass
  complete (4-surface integration map: DeepNSM SpoTriple shape + ractor
  ModuleEntry + AriGraph SpoRecord/TruthValue + ReasoningWitness64).
  Code lands next, with the 7-field shape: PatternMatchSpec +
  AtomTouchMask + MailboxSpawnSpec + RecipeComposition + EdgeEmissionSpec
  + EpisodicWitnessSpec + TemplateProvenance.
- D-ODOO-BP-1b — per-lane entity consts, L1–L15 Wave (one subagent per lane).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…ition over CausalEdge64 + Tactic + role_keys (E-SAVANT-COMPOSITION-1)

v1 (shipped PR #420 with a "MED on dispatch shape" caveat) review resolved:
the Reasoner trait surface fails CLAUDE.md "P-1 The Click" + "P0 AGI-as-glove"
litmus tests verbatim:

1. "new capability lands as a new column, not a new layer"
   → the Reasoner trait IS a new layer
2. "free function on a carrier's state = reject"
   → build_conclusion(savant, ctx) is the named anti-pattern
3. "wrap the axes in a new struct = breaks the SIMD sweep"
   → SavantConclusion + SavantSuggestion duplicate CausalEdge64

v2 routes the canonical path through the agnostic substrate that already exists:
- CausalEdge64 (zero-dep crates/causal-edge, v2 layout — the savant's decision
  IS the emitted edge: SPO palette + NARS truth + Pearl 2³ + inference mantissa)
- Tactic trait + 34 kernels (PR #411 — "the Elixir-like recipe layer that later
  fronts the real fingerprint substrate via cognitive-shader-driver with no
  change to the 34 call sites")
- 33-TSV atom layer (PR #411 contract::atoms::CANONICAL_ATOMS)
- Role-key catalogues (I-VSA-IDENTITIES names callcenter/role_keys.rs as future
  Layer-2 home)

Deliverables (Queued):
- D-ODOO-SAV-5a: SavantPattern + TacticInvocation + EdgeEmissionSpec +
  AtomTouchMask primitives in lance-graph-contract (Group D)
- D-ODOO-SAV-5b: callcenter/role_keys.rs with 25 disjoint Vsa16kF32 slices
  + lookup-by-OdooSavant + slice manifest (Group E)
- D-ODOO-SAV-5c: 25 typed SavantPattern consts drawn from
  .claude/odoo/savants/<N>.md slot 1/4 + .claude/odoo/L*.md (Group F)
- D-ODOO-SAV-5d: #[deprecated] + legacy-reasoner feature gate + migration
  pointers on v1 Reasoner surface, per I-LEGACY-API-FEATURE-GATED (Group G)
- D-ODOO-SAV-5e: end-to-end test (FiscalPositionResolver SavantPattern →
  expected CausalEdge64 row, SPO + NARS + v2 signed mantissa)

Execution: 5a + 5b parallel (additive, zero churn) → 5c after both → 5d after
5c (migration pointers name real targets) → 5e throughout. woa-rs consumer
migration OUT OF SCOPE but UNBLOCKED by 5d.

Board hygiene per CLAUDE.md: plan file + INTEGRATION_PLANS PREPEND +
STATUS_BOARD section + EPIPHANIES E-SAVANT-COMPOSITION-1 all in this commit.

v1 surface (Reasoner trait, 4 *Reasoner impls, SavantConclusion,
SavantSuggestion, build_conclusion) stays compiling under the legacy-reasoner
feature with #[deprecated] migration pointers until woa-rs migrates its
Reasoner::reason() call sites to SavantPattern resolution. Removal in a
follow-up PR after the migration.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…OO-SAV-5b 25 savant role keys

Two parallel additive deliverables — both zero-churn surfaces opened by
the v2 reshape (PR #420 deprecation) + blueprint plan (commits 741a25c
+ 6d2466e):

## D-ODOO-BP-1a — typed Odoo entity DTOs (lance-graph-ontology::odoo_blueprint)

New module `crates/lance-graph-ontology/src/odoo_blueprint.rs` carrying
the typed surface the OGIT → OWL → DOLCE → FIBU/FIBO inheritance chain
will operate on (replacing today's ad-hoc string-keyed maps against
`model_name`).

Types: OdooEntity + OdooField + OdooMethod + OdooDecorator +
OdooStateMachine + OdooState + OdooTransition + OdooConstraint +
OdooProvenance + OdooSourceRef.

Enums: OdooFieldKind (17 variants), OdooSemanticRole (12), OdooMethodKind
(10), OdooReturnKind (10), OdooDecoratorKind (7), OdooStateSemantic (7),
OdooConstraintKind (3), OdooConfidence (3 — Curated/Extracted/Conjecture).

Tests: 3 — sample_entity_compiles_as_const (FiscalPosition shape),
state_machine_entity_compiles (Invoice draft→posted→cancel),
empty_entity_compiles (zero case).

Per-lane consts (L1–L15) land in D-ODOO-BP-1b; OGIT/OWL/DOLCE/FIBU
wiring lands in 1c/d/e; source extraction in 1f; JITson + recipes in 1g.

## D-ODOO-SAV-5b — 25 savant role keys (contract::callcenter::role_keys)

New module `crates/lance-graph-contract/src/callcenter/{mod,role_keys}.rs`
per I-VSA-IDENTITIES Layer-2 catalogue doctrine (sibling of
`contract::grammar::role_keys`, NOT in the separate lance-graph-callcenter
crate — path correction from the v2 plan).

Slice layout: 25 savants × 90 dims = 2250 dims in the SMB headroom
[14096..16346), with 38 dims of headroom remaining. FNV-64 seeded from
each savant's name (lookup matches SAVANTS const order). LazyLock<[RoleKey; 25]>
lookup by id or name.

Tests: 7 — slices_disjoint_and_in_bounds, savant_zone_fits_in_smb_headroom
(2250 dims + 38 headroom = 2288), id_lookup_matches_name_lookup,
id_16_absent (the intentional gap), last_savant_is_backorder_judge,
deterministic_pseudo_random_bits, no_overlap_with_grammar_slices.

## Supporting change

`RoleKey::generate` made `pub` in `contract::grammar::role_keys` so
sibling per-domain Layer-2 catalogues (callcenter today; persona, others
later) can construct their own role keys with disjoint slice allocations
— per I-VSA-IDENTITIES. Documented in the doc-comment.

## Tests + integration

- lance-graph-contract: 7 new callcenter::role_keys tests pass; 454 prior
  contract lib tests unaffected
- lance-graph-ontology: 3 new odoo_blueprint tests pass; 43 lib tests total

## Followups

- D-ODOO-SAV-5a (BusinessGrammarTemplate primitive) — design pass
  complete (4-surface integration map: DeepNSM SpoTriple shape + ractor
  ModuleEntry + AriGraph SpoRecord/TruthValue + ReasoningWitness64).
  Code lands next, with the 7-field shape: PatternMatchSpec +
  AtomTouchMask + MailboxSpawnSpec + RecipeComposition + EdgeEmissionSpec
  + EpisodicWitnessSpec + TemplateProvenance.
- D-ODOO-BP-1b — per-lane entity consts, L1–L15 Wave (one subagent per lane).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…ition over CausalEdge64 + Tactic + role_keys (E-SAVANT-COMPOSITION-1)

v1 (shipped PR #420 with a "MED on dispatch shape" caveat) review resolved:
the Reasoner trait surface fails CLAUDE.md "P-1 The Click" + "P0 AGI-as-glove"
litmus tests verbatim:

1. "new capability lands as a new column, not a new layer"
   → the Reasoner trait IS a new layer
2. "free function on a carrier's state = reject"
   → build_conclusion(savant, ctx) is the named anti-pattern
3. "wrap the axes in a new struct = breaks the SIMD sweep"
   → SavantConclusion + SavantSuggestion duplicate CausalEdge64

v2 routes the canonical path through the agnostic substrate that already exists:
- CausalEdge64 (zero-dep crates/causal-edge, v2 layout — the savant's decision
  IS the emitted edge: SPO palette + NARS truth + Pearl 2³ + inference mantissa)
- Tactic trait + 34 kernels (PR #411 — "the Elixir-like recipe layer that later
  fronts the real fingerprint substrate via cognitive-shader-driver with no
  change to the 34 call sites")
- 33-TSV atom layer (PR #411 contract::atoms::CANONICAL_ATOMS)
- Role-key catalogues (I-VSA-IDENTITIES names callcenter/role_keys.rs as future
  Layer-2 home)

Deliverables (Queued):
- D-ODOO-SAV-5a: SavantPattern + TacticInvocation + EdgeEmissionSpec +
  AtomTouchMask primitives in lance-graph-contract (Group D)
- D-ODOO-SAV-5b: callcenter/role_keys.rs with 25 disjoint Vsa16kF32 slices
  + lookup-by-OdooSavant + slice manifest (Group E)
- D-ODOO-SAV-5c: 25 typed SavantPattern consts drawn from
  .claude/odoo/savants/<N>.md slot 1/4 + .claude/odoo/L*.md (Group F)
- D-ODOO-SAV-5d: #[deprecated] + legacy-reasoner feature gate + migration
  pointers on v1 Reasoner surface, per I-LEGACY-API-FEATURE-GATED (Group G)
- D-ODOO-SAV-5e: end-to-end test (FiscalPositionResolver SavantPattern →
  expected CausalEdge64 row, SPO + NARS + v2 signed mantissa)

Execution: 5a + 5b parallel (additive, zero churn) → 5c after both → 5d after
5c (migration pointers name real targets) → 5e throughout. woa-rs consumer
migration OUT OF SCOPE but UNBLOCKED by 5d.

Board hygiene per CLAUDE.md: plan file + INTEGRATION_PLANS PREPEND +
STATUS_BOARD section + EPIPHANIES E-SAVANT-COMPOSITION-1 all in this commit.

v1 surface (Reasoner trait, 4 *Reasoner impls, SavantConclusion,
SavantSuggestion, build_conclusion) stays compiling under the legacy-reasoner
feature with #[deprecated] migration pointers until woa-rs migrates its
Reasoner::reason() call sites to SavantPattern resolution. Removal in a
follow-up PR after the migration.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…OO-SAV-5b 25 savant role keys

Two parallel additive deliverables — both zero-churn surfaces opened by
the v2 reshape (PR #420 deprecation) + blueprint plan (commits 741a25c
+ 6d2466e):

## D-ODOO-BP-1a — typed Odoo entity DTOs (lance-graph-ontology::odoo_blueprint)

New module `crates/lance-graph-ontology/src/odoo_blueprint.rs` carrying
the typed surface the OGIT → OWL → DOLCE → FIBU/FIBO inheritance chain
will operate on (replacing today's ad-hoc string-keyed maps against
`model_name`).

Types: OdooEntity + OdooField + OdooMethod + OdooDecorator +
OdooStateMachine + OdooState + OdooTransition + OdooConstraint +
OdooProvenance + OdooSourceRef.

Enums: OdooFieldKind (17 variants), OdooSemanticRole (12), OdooMethodKind
(10), OdooReturnKind (10), OdooDecoratorKind (7), OdooStateSemantic (7),
OdooConstraintKind (3), OdooConfidence (3 — Curated/Extracted/Conjecture).

Tests: 3 — sample_entity_compiles_as_const (FiscalPosition shape),
state_machine_entity_compiles (Invoice draft→posted→cancel),
empty_entity_compiles (zero case).

Per-lane consts (L1–L15) land in D-ODOO-BP-1b; OGIT/OWL/DOLCE/FIBU
wiring lands in 1c/d/e; source extraction in 1f; JITson + recipes in 1g.

## D-ODOO-SAV-5b — 25 savant role keys (contract::callcenter::role_keys)

New module `crates/lance-graph-contract/src/callcenter/{mod,role_keys}.rs`
per I-VSA-IDENTITIES Layer-2 catalogue doctrine (sibling of
`contract::grammar::role_keys`, NOT in the separate lance-graph-callcenter
crate — path correction from the v2 plan).

Slice layout: 25 savants × 90 dims = 2250 dims in the SMB headroom
[14096..16346), with 38 dims of headroom remaining. FNV-64 seeded from
each savant's name (lookup matches SAVANTS const order). LazyLock<[RoleKey; 25]>
lookup by id or name.

Tests: 7 — slices_disjoint_and_in_bounds, savant_zone_fits_in_smb_headroom
(2250 dims + 38 headroom = 2288), id_lookup_matches_name_lookup,
id_16_absent (the intentional gap), last_savant_is_backorder_judge,
deterministic_pseudo_random_bits, no_overlap_with_grammar_slices.

## Supporting change

`RoleKey::generate` made `pub` in `contract::grammar::role_keys` so
sibling per-domain Layer-2 catalogues (callcenter today; persona, others
later) can construct their own role keys with disjoint slice allocations
— per I-VSA-IDENTITIES. Documented in the doc-comment.

## Tests + integration

- lance-graph-contract: 7 new callcenter::role_keys tests pass; 454 prior
  contract lib tests unaffected
- lance-graph-ontology: 3 new odoo_blueprint tests pass; 43 lib tests total

## Followups

- D-ODOO-SAV-5a (BusinessGrammarTemplate primitive) — design pass
  complete (4-surface integration map: DeepNSM SpoTriple shape + ractor
  ModuleEntry + AriGraph SpoRecord/TruthValue + ReasoningWitness64).
  Code lands next, with the 7-field shape: PatternMatchSpec +
  AtomTouchMask + MailboxSpawnSpec + RecipeComposition + EdgeEmissionSpec
  + EpisodicWitnessSpec + TemplateProvenance.
- D-ODOO-BP-1b — per-lane entity consts, L1–L15 Wave (one subagent per lane).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…ition over CausalEdge64 + Tactic + role_keys (E-SAVANT-COMPOSITION-1)

v1 (shipped PR #420 with a "MED on dispatch shape" caveat) review resolved:
the Reasoner trait surface fails CLAUDE.md "P-1 The Click" + "P0 AGI-as-glove"
litmus tests verbatim:

1. "new capability lands as a new column, not a new layer"
   → the Reasoner trait IS a new layer
2. "free function on a carrier's state = reject"
   → build_conclusion(savant, ctx) is the named anti-pattern
3. "wrap the axes in a new struct = breaks the SIMD sweep"
   → SavantConclusion + SavantSuggestion duplicate CausalEdge64

v2 routes the canonical path through the agnostic substrate that already exists:
- CausalEdge64 (zero-dep crates/causal-edge, v2 layout — the savant's decision
  IS the emitted edge: SPO palette + NARS truth + Pearl 2³ + inference mantissa)
- Tactic trait + 34 kernels (PR #411 — "the Elixir-like recipe layer that later
  fronts the real fingerprint substrate via cognitive-shader-driver with no
  change to the 34 call sites")
- 33-TSV atom layer (PR #411 contract::atoms::CANONICAL_ATOMS)
- Role-key catalogues (I-VSA-IDENTITIES names callcenter/role_keys.rs as future
  Layer-2 home)

Deliverables (Queued):
- D-ODOO-SAV-5a: SavantPattern + TacticInvocation + EdgeEmissionSpec +
  AtomTouchMask primitives in lance-graph-contract (Group D)
- D-ODOO-SAV-5b: callcenter/role_keys.rs with 25 disjoint Vsa16kF32 slices
  + lookup-by-OdooSavant + slice manifest (Group E)
- D-ODOO-SAV-5c: 25 typed SavantPattern consts drawn from
  .claude/odoo/savants/<N>.md slot 1/4 + .claude/odoo/L*.md (Group F)
- D-ODOO-SAV-5d: #[deprecated] + legacy-reasoner feature gate + migration
  pointers on v1 Reasoner surface, per I-LEGACY-API-FEATURE-GATED (Group G)
- D-ODOO-SAV-5e: end-to-end test (FiscalPositionResolver SavantPattern →
  expected CausalEdge64 row, SPO + NARS + v2 signed mantissa)

Execution: 5a + 5b parallel (additive, zero churn) → 5c after both → 5d after
5c (migration pointers name real targets) → 5e throughout. woa-rs consumer
migration OUT OF SCOPE but UNBLOCKED by 5d.

Board hygiene per CLAUDE.md: plan file + INTEGRATION_PLANS PREPEND +
STATUS_BOARD section + EPIPHANIES E-SAVANT-COMPOSITION-1 all in this commit.

v1 surface (Reasoner trait, 4 *Reasoner impls, SavantConclusion,
SavantSuggestion, build_conclusion) stays compiling under the legacy-reasoner
feature with #[deprecated] migration pointers until woa-rs migrates its
Reasoner::reason() call sites to SavantPattern resolution. Removal in a
follow-up PR after the migration.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…OO-SAV-5b 25 savant role keys

Two parallel additive deliverables — both zero-churn surfaces opened by
the v2 reshape (PR #420 deprecation) + blueprint plan (commits 741a25c
+ 6d2466e):

## D-ODOO-BP-1a — typed Odoo entity DTOs (lance-graph-ontology::odoo_blueprint)

New module `crates/lance-graph-ontology/src/odoo_blueprint.rs` carrying
the typed surface the OGIT → OWL → DOLCE → FIBU/FIBO inheritance chain
will operate on (replacing today's ad-hoc string-keyed maps against
`model_name`).

Types: OdooEntity + OdooField + OdooMethod + OdooDecorator +
OdooStateMachine + OdooState + OdooTransition + OdooConstraint +
OdooProvenance + OdooSourceRef.

Enums: OdooFieldKind (17 variants), OdooSemanticRole (12), OdooMethodKind
(10), OdooReturnKind (10), OdooDecoratorKind (7), OdooStateSemantic (7),
OdooConstraintKind (3), OdooConfidence (3 — Curated/Extracted/Conjecture).

Tests: 3 — sample_entity_compiles_as_const (FiscalPosition shape),
state_machine_entity_compiles (Invoice draft→posted→cancel),
empty_entity_compiles (zero case).

Per-lane consts (L1–L15) land in D-ODOO-BP-1b; OGIT/OWL/DOLCE/FIBU
wiring lands in 1c/d/e; source extraction in 1f; JITson + recipes in 1g.

## D-ODOO-SAV-5b — 25 savant role keys (contract::callcenter::role_keys)

New module `crates/lance-graph-contract/src/callcenter/{mod,role_keys}.rs`
per I-VSA-IDENTITIES Layer-2 catalogue doctrine (sibling of
`contract::grammar::role_keys`, NOT in the separate lance-graph-callcenter
crate — path correction from the v2 plan).

Slice layout: 25 savants × 90 dims = 2250 dims in the SMB headroom
[14096..16346), with 38 dims of headroom remaining. FNV-64 seeded from
each savant's name (lookup matches SAVANTS const order). LazyLock<[RoleKey; 25]>
lookup by id or name.

Tests: 7 — slices_disjoint_and_in_bounds, savant_zone_fits_in_smb_headroom
(2250 dims + 38 headroom = 2288), id_lookup_matches_name_lookup,
id_16_absent (the intentional gap), last_savant_is_backorder_judge,
deterministic_pseudo_random_bits, no_overlap_with_grammar_slices.

## Supporting change

`RoleKey::generate` made `pub` in `contract::grammar::role_keys` so
sibling per-domain Layer-2 catalogues (callcenter today; persona, others
later) can construct their own role keys with disjoint slice allocations
— per I-VSA-IDENTITIES. Documented in the doc-comment.

## Tests + integration

- lance-graph-contract: 7 new callcenter::role_keys tests pass; 454 prior
  contract lib tests unaffected
- lance-graph-ontology: 3 new odoo_blueprint tests pass; 43 lib tests total

## Followups

- D-ODOO-SAV-5a (BusinessGrammarTemplate primitive) — design pass
  complete (4-surface integration map: DeepNSM SpoTriple shape + ractor
  ModuleEntry + AriGraph SpoRecord/TruthValue + ReasoningWitness64).
  Code lands next, with the 7-field shape: PatternMatchSpec +
  AtomTouchMask + MailboxSpawnSpec + RecipeComposition + EdgeEmissionSpec
  + EpisodicWitnessSpec + TemplateProvenance.
- D-ODOO-BP-1b — per-lane entity consts, L1–L15 Wave (one subagent per lane).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…d (P1 review on #420)

The SavantConclusion dropped the `suggestion` field the plan + per-savant specs
require (.claude/plans/odoo-savant-reasoners-v1.md:80,
.claude/odoo/savants/FiscalPositionResolver.md:84), so a consumer could rank by
confidence but not see *which* decision was delegated.

Add `pub enum SavantSuggestion` (callcenter type, contract untouched — the vow
stays pure, no serde / no JSON) with variants for the AXIS-B decision shapes
across the 25 savants:
- SelectFromTable { candidate_table } — top-1 entity (FiscalPosition, Pricelist,
  Account, AnalyticModel, ProcurementRule, RouteTiebreaker, BankStatementMatch)
- Gate — yes/no (Autopost, PaymentToInvoiceMatch, Upsell, BackorderJudge)
- Anomaly — suspect key (SequenceGap)
- AdvancePeriod — next open period (LockDate, ReorderTiming)
- PolicyChoice — fixed-set variant (TaxExigibility, ReportRateType,
  PartnerTrust, UserCompanyAccess, RemovalStrategy)
- Distribution { over_table } — weighted (AnalyticDistribution, ReplenishmentReport)
- RankedSet { from_table } — top-N (ReconcileMatch, MoveAssignmentPrioritizer,
  CurrencySelection)

`suggestion_for(savant)` maps every roster id → its variant; reasoners populate
it via `build_conclusion`. The row-level value (the concrete fiscal_position_id,
etc.) is the consumer's mechanical resolution over rows it holds — the same
boundary the 14 NEEDS-INPUT savants already document. The conclusion now
CARRIES the AXIS-B decision shape so the delegation is actionable.

Tests: 2 new (suggestion_shape_per_savant + every_savant_has_a_suggestion_shape
covering all 25 roster ids); 8 prior savant_reasoners tests pass; 137 callcenter
tests pass; zone_serialize_check no-JSON guard clean.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI added a commit that referenced this pull request May 28, 2026
…att-k2pHI

callcenter: D-ODOO-SAV-4 odoo-savant reasoners + audit/Policy fixes
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…ition over CausalEdge64 + Tactic + role_keys (E-SAVANT-COMPOSITION-1)

v1 (shipped PR #420 with a "MED on dispatch shape" caveat) review resolved:
the Reasoner trait surface fails CLAUDE.md "P-1 The Click" + "P0 AGI-as-glove"
litmus tests verbatim:

1. "new capability lands as a new column, not a new layer"
   → the Reasoner trait IS a new layer
2. "free function on a carrier's state = reject"
   → build_conclusion(savant, ctx) is the named anti-pattern
3. "wrap the axes in a new struct = breaks the SIMD sweep"
   → SavantConclusion + SavantSuggestion duplicate CausalEdge64

v2 routes the canonical path through the agnostic substrate that already exists:
- CausalEdge64 (zero-dep crates/causal-edge, v2 layout — the savant's decision
  IS the emitted edge: SPO palette + NARS truth + Pearl 2³ + inference mantissa)
- Tactic trait + 34 kernels (PR #411 — "the Elixir-like recipe layer that later
  fronts the real fingerprint substrate via cognitive-shader-driver with no
  change to the 34 call sites")
- 33-TSV atom layer (PR #411 contract::atoms::CANONICAL_ATOMS)
- Role-key catalogues (I-VSA-IDENTITIES names callcenter/role_keys.rs as future
  Layer-2 home)

Deliverables (Queued):
- D-ODOO-SAV-5a: SavantPattern + TacticInvocation + EdgeEmissionSpec +
  AtomTouchMask primitives in lance-graph-contract (Group D)
- D-ODOO-SAV-5b: callcenter/role_keys.rs with 25 disjoint Vsa16kF32 slices
  + lookup-by-OdooSavant + slice manifest (Group E)
- D-ODOO-SAV-5c: 25 typed SavantPattern consts drawn from
  .claude/odoo/savants/<N>.md slot 1/4 + .claude/odoo/L*.md (Group F)
- D-ODOO-SAV-5d: #[deprecated] + legacy-reasoner feature gate + migration
  pointers on v1 Reasoner surface, per I-LEGACY-API-FEATURE-GATED (Group G)
- D-ODOO-SAV-5e: end-to-end test (FiscalPositionResolver SavantPattern →
  expected CausalEdge64 row, SPO + NARS + v2 signed mantissa)

Execution: 5a + 5b parallel (additive, zero churn) → 5c after both → 5d after
5c (migration pointers name real targets) → 5e throughout. woa-rs consumer
migration OUT OF SCOPE but UNBLOCKED by 5d.

Board hygiene per CLAUDE.md: plan file + INTEGRATION_PLANS PREPEND +
STATUS_BOARD section + EPIPHANIES E-SAVANT-COMPOSITION-1 all in this commit.

v1 surface (Reasoner trait, 4 *Reasoner impls, SavantConclusion,
SavantSuggestion, build_conclusion) stays compiling under the legacy-reasoner
feature with #[deprecated] migration pointers until woa-rs migrates its
Reasoner::reason() call sites to SavantPattern resolution. Removal in a
follow-up PR after the migration.

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
AdaWorldAPI pushed a commit that referenced this pull request May 28, 2026
…OO-SAV-5b 25 savant role keys

Two parallel additive deliverables — both zero-churn surfaces opened by
the v2 reshape (PR #420 deprecation) + blueprint plan (commits 741a25c
+ 6d2466e):

## D-ODOO-BP-1a — typed Odoo entity DTOs (lance-graph-ontology::odoo_blueprint)

New module `crates/lance-graph-ontology/src/odoo_blueprint.rs` carrying
the typed surface the OGIT → OWL → DOLCE → FIBU/FIBO inheritance chain
will operate on (replacing today's ad-hoc string-keyed maps against
`model_name`).

Types: OdooEntity + OdooField + OdooMethod + OdooDecorator +
OdooStateMachine + OdooState + OdooTransition + OdooConstraint +
OdooProvenance + OdooSourceRef.

Enums: OdooFieldKind (17 variants), OdooSemanticRole (12), OdooMethodKind
(10), OdooReturnKind (10), OdooDecoratorKind (7), OdooStateSemantic (7),
OdooConstraintKind (3), OdooConfidence (3 — Curated/Extracted/Conjecture).

Tests: 3 — sample_entity_compiles_as_const (FiscalPosition shape),
state_machine_entity_compiles (Invoice draft→posted→cancel),
empty_entity_compiles (zero case).

Per-lane consts (L1–L15) land in D-ODOO-BP-1b; OGIT/OWL/DOLCE/FIBU
wiring lands in 1c/d/e; source extraction in 1f; JITson + recipes in 1g.

## D-ODOO-SAV-5b — 25 savant role keys (contract::callcenter::role_keys)

New module `crates/lance-graph-contract/src/callcenter/{mod,role_keys}.rs`
per I-VSA-IDENTITIES Layer-2 catalogue doctrine (sibling of
`contract::grammar::role_keys`, NOT in the separate lance-graph-callcenter
crate — path correction from the v2 plan).

Slice layout: 25 savants × 90 dims = 2250 dims in the SMB headroom
[14096..16346), with 38 dims of headroom remaining. FNV-64 seeded from
each savant's name (lookup matches SAVANTS const order). LazyLock<[RoleKey; 25]>
lookup by id or name.

Tests: 7 — slices_disjoint_and_in_bounds, savant_zone_fits_in_smb_headroom
(2250 dims + 38 headroom = 2288), id_lookup_matches_name_lookup,
id_16_absent (the intentional gap), last_savant_is_backorder_judge,
deterministic_pseudo_random_bits, no_overlap_with_grammar_slices.

## Supporting change

`RoleKey::generate` made `pub` in `contract::grammar::role_keys` so
sibling per-domain Layer-2 catalogues (callcenter today; persona, others
later) can construct their own role keys with disjoint slice allocations
— per I-VSA-IDENTITIES. Documented in the doc-comment.

## Tests + integration

- lance-graph-contract: 7 new callcenter::role_keys tests pass; 454 prior
  contract lib tests unaffected
- lance-graph-ontology: 3 new odoo_blueprint tests pass; 43 lib tests total

## Followups

- D-ODOO-SAV-5a (BusinessGrammarTemplate primitive) — design pass
  complete (4-surface integration map: DeepNSM SpoTriple shape + ractor
  ModuleEntry + AriGraph SpoRecord/TruthValue + ReasoningWitness64).
  Code lands next, with the 7-field shape: PatternMatchSpec +
  AtomTouchMask + MailboxSpawnSpec + RecipeComposition + EdgeEmissionSpec
  + EpisodicWitnessSpec + TemplateProvenance.
- D-ODOO-BP-1b — per-lane entity consts, L1–L15 Wave (one subagent per lane).

https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants